﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Data;

namespace DBF_ReadWrite
{
    class FileReadWriter
    {

        public DBF_Struct Reader(FileStream FS, string pDBF_FileName)
        {

            return Abstract_Reader(FS,pDBF_FileName);
        }
        
        public DBF_Struct Reader(string pDBF_FileName)
        {
            FileStream FS;

            FS = new FileStream(pDBF_FileName, FileMode.Open, FileAccess.Read);

            return Abstract_Reader(FS,pDBF_FileName);
        }

        private DBF_Struct Abstract_Reader(FileStream FS, string pDBF_FileName)
        {
            DBF_Struct pDBF_Struct;
            DataTable pDBF_DT = new DataTable();


            BinaryReader Read_FS = new BinaryReader(FS);

            DBF_Struct.DBF_Header pDBF_Header = new DBF_Struct.DBF_Header();

            //   Header 읽기
            pDBF_Header.version = Read_FS.ReadChar();
            pDBF_Header.year = Read_FS.ReadChar();
            pDBF_Header.month = Read_FS.ReadChar();
            pDBF_Header.day = Read_FS.ReadChar();
            pDBF_Header.recordn = Read_FS.ReadInt32();
            pDBF_Header.header_length = Read_FS.ReadInt16();
            pDBF_Header.record_length = Read_FS.ReadInt16();
            pDBF_Header.ver10_reserved = Read_FS.ReadChars(20);
            //  Header 읽기 끝

            //  필드 개수 계산
            int nfield = (pDBF_Header.header_length - 32) / 32;

            List<DBF_Struct.DBF_Field_description> pFieldDescription = new List<DBF_Struct.DBF_Field_description>();

            for (int i = 0; i < nfield; i++)
            {
                DBF_Struct.DBF_Field_description ptFieldDescription = new DBF_Struct.DBF_Field_description();

                byte[] temp_name = new byte[11];
                // 한글처리로 인하여 Read_FS.ReadChars(11) 사용못함
                ptFieldDescription.name = Encoding.Default.GetString(Read_FS.ReadBytes(11));

                ptFieldDescription.type = Read_FS.ReadChar();
                ptFieldDescription.field_data_adress = Read_FS.ReadInt32();
                //ptFieldDescription.field_length = Convert.ToChar(Read_FS.ReadByte());
                ptFieldDescription.field_length = Read_FS.ReadByte();
                ptFieldDescription.decimal_count = Read_FS.ReadChar();
                ptFieldDescription.ver10_reserved = Read_FS.ReadChars(14);

                pFieldDescription.Add(ptFieldDescription);

            }

            byte temp_End_FieldDescription = Read_FS.ReadByte(); // 0x0D 건너띄기

            string[,] DBF_record = new string[pDBF_Header.recordn, nfield];

            for (int i = 0; i < pDBF_Header.recordn; i++)
            {
                byte temp_Start_Record = Read_FS.ReadByte(); // 첫 삭제 바이트 건너띄기

                for (int j = 0; j < nfield; j++)
                {
                    byte[] temp_record = new byte[pFieldDescription[j].field_length];
                    temp_record = Read_FS.ReadBytes(pFieldDescription[j].field_length);
                    DBF_record[i, j] = Encoding.Default.GetString(temp_record).Replace(" ", "");
                }
            }

            Read_FS.Close();
            FS.Dispose();
            FS.Close();

            pDBF_DT = Make_DT(pDBF_Header, pFieldDescription, nfield, DBF_record);

            pDBF_Struct = new DBF_Struct(pDBF_FileName, pDBF_Header, pFieldDescription, pDBF_DT);

            return pDBF_Struct;
        }
        //======  테이블 생성

        private DataTable Make_DT(DBF_Struct.DBF_Header pDBF_Header, List<DBF_Struct.DBF_Field_description> pFieldDescription, int nfield, string[,] DBF_record)
        {
            
            DataTable table = new DataTable();
            DataColumn column;
            DataRow row;

            column = new DataColumn();
            column.DataType = System.Type.GetType("System.Int32");
            column.ColumnName = "FID";
            column.ReadOnly = true;
            column.Unique = true;

            table.Columns.Add(column);

            for (int i = 0; i < nfield; i++)
            {
                column = new DataColumn();

                if (pFieldDescription[i].type == 'F')
                {
                    column.DataType = System.Type.GetType("System.Double");
                }
                else if (pFieldDescription[i].type == 'N')
                {
                    column.DataType = System.Type.GetType("System.Decimal");
                }
                else if (pFieldDescription[i].type == 'C')
                {
                    column.DataType = System.Type.GetType("System.String");
                }

                column.ColumnName = pFieldDescription[i].name.Replace("\0", "");
                column.AutoIncrement = false;
                column.Caption = pFieldDescription[i].name;
                column.ReadOnly = false;
                column.Unique = false;

                table.Columns.Add(column);
            }


            DataSet pdataSet = new DataSet();

            pdataSet.Tables.Add(table);

            for (int i = 0; i < pDBF_Header.recordn; i++)
            {
                row = table.NewRow();
                row[0] = i;

                for (int j = 0; j < nfield; j++)
                {

                    string Temp_DBF_Record = DBF_record[i, j];

                    if (Temp_DBF_Record.Contains("\0"))
                        Temp_DBF_Record = "";

                    if (pFieldDescription[j].type == 'C')
                    {
                        row[j + 1] = Temp_DBF_Record;
                    }
                    else
                    {
                        if (Temp_DBF_Record == "")
                            row[j + 1] = 0m;
                        else if (Temp_DBF_Record.Contains("e") || Temp_DBF_Record.Contains("E"))
                            row[j + 1] = Convert.ToDouble(Temp_DBF_Record);
                        else
                            row[j + 1] = Convert.ToDecimal(Temp_DBF_Record);
                    }
                }
                table.Rows.Add(row);
            }

            return table;
        }

        public bool Writer(DBF_Struct pDBF_Struct, FileStream FS)
        {
            return abstract_Writer(pDBF_Struct, FS);
        }

        public bool Writer(DBF_Struct pDBF_Struct)
        {
            FileStream FS;

            FS = new FileStream(pDBF_Struct.pFileName, FileMode.Create, FileAccess.Write);

            return abstract_Writer(pDBF_Struct,FS);
        }

        public bool Writer(DBF_Struct pDBF_Struct, string pFileName)
        {
            FileStream FS;

            FS = new FileStream(pFileName, FileMode.Create, FileAccess.Write);

            return abstract_Writer(pDBF_Struct, FS);
        }

        private bool abstract_Writer(DBF_Struct pDBF_Struct, FileStream FS)
        {

            DataTable pDataTable = pDBF_Struct.pDataTable;
          
            BinaryWriter Write_FS = new BinaryWriter(FS);
            
            // Header 쓰기
            Write_FS.Write(pDBF_Struct.pHeader.version);
            Write_FS.Write(pDBF_Struct.pHeader.year);
            Write_FS.Write(pDBF_Struct.pHeader.month);
            Write_FS.Write(pDBF_Struct.pHeader.day);
            Write_FS.Write(pDBF_Struct.pHeader.recordn);
            Write_FS.Write(pDBF_Struct.pHeader.header_length);
            Write_FS.Write(pDBF_Struct.pHeader.record_length);
            Write_FS.Write(pDBF_Struct.pHeader.ver10_reserved);

            // Field_Description 쓰기
            int nfield = (pDBF_Struct.pHeader.header_length - 32) / 32;

            for (int i = 0; i < nfield; i++)
            {
                Write_FS.Write(Encoding.Default.GetBytes(pDBF_Struct.pField_description[i].name));
                Write_FS.Write(pDBF_Struct.pField_description[i].type);
                Write_FS.Write(pDBF_Struct.pField_description[i].field_data_adress);
                Write_FS.Write(pDBF_Struct.pField_description[i].field_length);
                Write_FS.Write(pDBF_Struct.pField_description[i].decimal_count);
                Write_FS.Write(pDBF_Struct.pField_description[i].ver10_reserved);
            }
            byte temp_End_FieldDescription = (byte)13;
            Write_FS.Write(temp_End_FieldDescription);

            // Data 쓰기

            for (int i = 0; i < pDBF_Struct .pHeader .recordn; i++)
            {
                byte temp_Start_Byte = (byte)32;
                Write_FS.Write(temp_Start_Byte);

                for (int j = 0; j < nfield; j++)
                {
                    byte[] Temp_ByteData = Encoding.Default.GetBytes(pDBF_Struct.pDataTable.Rows[i][j+1].ToString());
                    byte[] Temp_ByteDateWithSpace = new byte[pDBF_Struct.pField_description[j].field_length];

                    for (int k = 0; k < Temp_ByteDateWithSpace .Length ; k++)
                    {
                        if (pDBF_Struct.pField_description[j].type == 'C')
                        {
                            if (k < Temp_ByteData.Length)
                            {
                                Temp_ByteDateWithSpace[k] = Temp_ByteData[k];
                            }

                            else
                            {
                                Temp_ByteDateWithSpace[k] = (byte)32;
                            }
                        }
                        else
                        {
                            if (k > Temp_ByteDateWithSpace.Length - Temp_ByteData.Length -1)
                            {
                                Temp_ByteDateWithSpace[k] = Temp_ByteData[k - (Temp_ByteDateWithSpace.Length - Temp_ByteData.Length -1) -1];
                            }

                            else
                            {
                                Temp_ByteDateWithSpace[k] = (byte)32;
                            }
                        }
                    }

                    Write_FS.Write(Temp_ByteDateWithSpace);
                }
            }

            // 파일 종료 Byte(1A&h)

            byte temp_End_Write_Byte = (byte)26;
            Write_FS.Write(temp_End_Write_Byte);

            Write_FS.Close();
            FS.Dispose();
            FS.Close();

            return true;
        }
    }
}
